package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"../pb"
	"../util"
	"github.com/howeyc/gopass"
)

type Controller struct {
	BasePath   string
	InfoFile   string
	LatnId     string
	user       string
	passwd     string
	Config     *Configure
	Dispatcher *Leader

	Files      map[string]int
	FailedFile map[string]string
	Total      int
	Successful int
	Failed     int

	FeedBack chan FeedBack
	stop     chan interface{}
}

func NewController() *Controller {
	return &Controller{
		Files:      make(map[string]int),
		FailedFile: make(map[string]string),
		stop:       make(chan interface{}),
		FeedBack:   make(chan FeedBack),
	}
}

func (ctrl *Controller) LoadConfig(configPath string) error {
	conf, err := LoadConfig(configPath)
	if err != nil {
		return err
	}
	ctrl.Config = conf
	return nil
}

func (ctrl *Controller) InputUserInfo() {
	var latnId, user, storePath, infoFile string
	fmt.Println("请输入区号（如广州：200）：")
	fmt.Scanf("%s\n", &latnId)
	ctrl.LatnId = latnId

	fmt.Println("\r\n请输入用户名：")
	fmt.Scanf("%s\n", &user)
	ctrl.user = user

	fmt.Println("\r\n请输入密码：")
	passwd, err := gopass.GetPasswdMasked()
	if err != nil {
		fmt.Printf("[Error] 获取密码失败：%v\r\n", err)
		return
	}
	ctrl.passwd = string(passwd)

	fmt.Println("\r\n请输入下载文件保存的目录，默认保存在'download'：")
	fmt.Scanf("%s\n", &storePath)
	if strings.TrimSpace(storePath) == "" {
		storePath = "download"
	}
	now := time.Now().Format("150405")
	ctrl.BasePath = storePath + "-" + now

	fmt.Println("\r\n请输入包含路径信息的文件名，默认读取'data.txt'：")
	fmt.Scanf("%s\n", &infoFile)
	if strings.TrimSpace(infoFile) == "" {
		infoFile = "data.txt"
	}
	ctrl.InfoFile = infoFile
}

func (ctrl *Controller) CheckUpdate(version string) error {
	timestamp := strconv.FormatInt(time.Now().UnixNano()/1000/1000, 10)
	signature := util.MD5([]byte(ctrl.user + ctrl.passwd + timestamp))
	url := fmt.Sprintf(
		"http://%s:%s/rfdService?method=checkUpdate&user=%s&signature=%s&timestamp=%s&version=%s",
		ctrl.Config.ServerIP, ctrl.Config.AdminPort, ctrl.user, signature, timestamp, version)
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("[Error] 检查版本失败：%v", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return fmt.Errorf("[Error] 认证失败：%s", string(body))
	}
	if string(body) != version {
		return fmt.Errorf("[Error] 您的客户端版本需要更新：%s", string(body))
	}
	return nil
}

func (ctrl *Controller) CheckAvailable() error {
	timestamp := strconv.FormatInt(time.Now().UnixNano()/1000/1000, 10)
	signature := util.MD5([]byte(ctrl.user + ctrl.passwd + timestamp))
	url := fmt.Sprintf(
		"http://%s:%s/rfdService?method=checkAvailable&user=%s&signature=%s&timestamp=%s&latnId=%s",
		ctrl.Config.ServerIP, ctrl.Config.AdminPort, ctrl.user, signature, timestamp, ctrl.LatnId)
	resp, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("[Error] 连接服务器失败：%v", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return fmt.Errorf("[Error] %s", string(body))
	}
	return nil
}

func (ctrl *Controller) SetDispatcher(cli pb.RfdServiceClient) {
	ctrl.Dispatcher = NewLeader(cli)
	ctrl.Dispatcher.Init(ctrl.BasePath, ctrl.FeedBack)
}

func (ctrl *Controller) DownloadFile() error {
	log.Printf("创建文件夹 %s\r\n", ctrl.BasePath)
	if _, err := os.Stat(ctrl.BasePath); err == nil {
		return fmt.Errorf("[Error] 目录已存在，请更改保存目录: %v", ctrl.BasePath)
	}
	if err := os.MkdirAll(ctrl.BasePath, 0777); err != nil {
		log.Printf("[Error] 创建目录失败\r\n")
		return fmt.Errorf("[Error] Controller.Init.Mkdir: %v", err)
	}
	log.Printf("读取文件路径信息：%s\r\n", ctrl.InfoFile)
	return util.ReadAndHandleLine(ctrl.InfoFile, func(row string, finished bool) {
		if row != "" {
			ctrl.Total++
			if _, exist := ctrl.Files[row]; exist == true {
				log.Printf("检查到重复路径信息：%s\r\n", row)
				ctrl.Files[row]++
			} else {
				ctrl.Files[row] = 1
				ctrl.Dispatcher.Dispatch(row, ctrl.LatnId)
			}
		}
		if finished {
			log.Printf("文件信息读取完毕\r\n")
			ctrl.Dispatcher.Stop()
			ctrl.stop <- "true"
			return
		}
	})
}

func (ctrl *Controller) ListenFeedback() {
	for {
		select {
		case err := <-ctrl.FeedBack:
			if err.desc == "ok" {
				ctrl.Successful++
			} else {
				log.Printf("[Error] 传输失败: %s，区号：%s。请检查文件路径和区号。\r\n", err.file, err.latnId)
				log.Printf("[Error] Controller.ListenFeedback: fail to transfer %s, latnId: %s, desc:%s\r\n", err.file, err.latnId, err.desc)
				ctrl.FailedFile[err.file] = err.desc
				ctrl.Failed++
			}

		case <-ctrl.stop:
			return
		}
	}
}

func (ctrl *Controller) Finish() {
	now := time.Now().Format("150405")
	file, err := os.OpenFile("result-"+now+".txt", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		log.Printf("[Error] 打开文件失败：result.txt\r\n")
		return
	}
	ffile, err := os.OpenFile("fail-"+now+".txt", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666)
	if err != nil {
		log.Printf("[Error] 打开文件失败：fail.txt\r\n")
		return
	}
	defer file.Close()
	defer ffile.Close()
	s := "\r\n【结果】\r\n共%d条文件信息，传输成功：%d， 传输失败：%d，重复文件：%d\r\n\r\n"

	s = s + "【重复文件】: %d\r\n"
	duplicate := 0
	for file, count := range ctrl.Files {
		if count != 1 {
			duplicate++
			s = s + file + ": " + strconv.Itoa(count) + "\r\n"
		}
	}
	s = s + "\r\n【失败文件】: %d\r\n"
	for file, desc := range ctrl.FailedFile {
		ffile.WriteString(file + "\r\n")
		s = s + file + ": " + desc + "\r\n"
	}
	s = s + "\r\n"
	msg := fmt.Sprintf(s, ctrl.Total, ctrl.Successful, ctrl.Failed, duplicate, duplicate, ctrl.Failed)
	file.WriteString(msg)
	log.Printf(msg)
}
